feat: animations - #45
Merged
Merged
Conversation
AhmedAmrNabil
force-pushed
the
feat/animation
branch
from
April 19, 2026 18:25
7fa1911 to
3bec32f
Compare
AhmedAmrNabil
force-pushed
the
feat/animation
branch
2 times, most recently
from
April 20, 2026 20:06
6bcfbb3 to
7053363
Compare
AhmedAmrNabil
marked this pull request as ready for review
April 21, 2026 00:42
There was a problem hiding this comment.
Pull request overview
This PR adds initial support for skeletal (skinning) and node-based animations by extending the model loader to import animation/skeleton data, updating the renderer/shaders to upload bone matrices via a UBO, and introducing ECS animation component/system wiring.
Changes:
- Import skeleton + animation clips from Assimp into
Model, and addAnimationComponent+AnimationSystemto driveAnimatorupdates per-frame. - Extend
ForwardRendererandlit.vertto support skinned rendering via aBonesuniform block and per-draw bone matrix uploads. - Add a new animation playground config demonstrating animated model usage.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/states/play-state.hpp | Runs the new AnimationSystem during the play loop. |
| src/common/uniform-buffer/uniform-buffer.hpp | Adds a small UBO wrapper used for bone matrices. |
| src/common/systems/forward-renderer.hpp | Extends render command data to optionally carry an Animator and adds a bones UBO. |
| src/common/systems/forward-renderer.cpp | Uploads bone matrices and applies node transforms during command building. |
| src/common/systems/animation-system.hpp | Declares the new ECS animation system. |
| src/common/systems/animation-system.cpp | Updates animators per-entity and handles default clip replay when finished. |
| src/common/shader/shader.hpp | Adds bindUniformBlock() helper for UBO binding. |
| src/common/model/model.hpp | Adds skeleton/animation storage and bone data processing helpers. |
| src/common/model/model.cpp | Loads animations/skeleton nodes and populates per-vertex bone IDs/weights. |
| src/common/mesh/mesh.hpp | Adds vertex attribute locations for bone IDs/weights (and a new include). |
| src/common/components/mesh-renderer.hpp | Stores nodeName and hasBones for renderer animation decisions. |
| src/common/components/component-deserializer.hpp | Registers the new AnimationComponent. |
| src/common/components/animation.hpp | Defines AnimationComponent and clip mapping. |
| src/common/components/animation.cpp | Implements JSON deserialization and clip playback. |
| src/common/animation/skeleton.hpp | Adds skeleton node graph + bone pose lookup. |
| src/common/animation/bone.hpp | Adds bone keyframe interpolation support. |
| src/common/animation/animator.hpp | Adds animator runtime for skeletal + node transform evaluation. |
| src/common/animation/animation.hpp | Adds Animation container to load Assimp channels. |
| config/playgrounds/animation.jsonc | Adds a sample scene showcasing animated model usage. |
| assets/shaders/lit.vert | Adds skinning path and Bones UBO support in the lit vertex shader. |
| CMakeLists.txt | Adds new animation component/system sources to the build. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
AhmedAmrNabil
force-pushed
the
feat/animation
branch
from
April 23, 2026 02:51
ce8d241 to
8079627
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 28 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
AhmedAmrNabil
force-pushed
the
feat/animation
branch
from
April 23, 2026 21:39
c7282e3 to
f32c8e5
Compare
AhmedAmrNabil
commented
Apr 23, 2026
AhmedSobhy01
previously approved these changes
Apr 24, 2026
AhmedAmrNabil
force-pushed
the
feat/animation
branch
from
April 25, 2026 12:37
4ead262 to
e1b553c
Compare
AhmedSobhy01
approved these changes
Apr 25, 2026
- Implemented Animation, Animator, Bone, and Skeleton classes to handle skeletal animations. - Added AnimationComponent to manage animations within entities. - Integrated animation loading in the Model class, supporting multiple animations. - Created AnimationSystem to update animations based on input and delta time. - Enhanced ForwardRenderer to support animated meshes with bone transformations. - Introduced UniformBuffer class for efficient bone matrix handling in shaders. - Updated MeshRendererComponent to indicate if a mesh uses bones for skinning. - Added deserialization for AnimationComponent to load animation data from JSON.
…ove commented code
…lobal inverse matrix
…med playback of animations
…ormBuffer constructor and update method
… avoid division by zero
…rove isFinished logic
AhmedSobhy01
force-pushed
the
feat/animation
branch
from
April 25, 2026 13:33
e1b553c to
c540d7c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WARNING: 1000 lines for new configs only, the pr is only 1000 lines
Animation system overview
The system has four files with clear responsibilities:
bone.hpphandles keyframe data and interpolation,skeleton.hppowns the bone registry and node hierarchy,animation.hppwraps an assimp animation into channels, andanimator.hppdrives playback each frame.bone.hpp—BoneAnimationEach bone's animation is stored as three independent keyframe tracks: positions (
glm::vec3), rotations (glm::quat), scales (glm::vec3). Theinterpolate(t)method finds the two keyframes bracketing the current time viafindIndex, computes a normalized factor between them, then runsglm::mixfor position/scale andglm::slerpfor rotation (slerp takes the shortest arc on the quaternion sphere). The result is composed into a singlemat4via translate × rotate × scale.skeleton.hpp—Skeleton+SkeletonNodeSkeletonholds two things: aboneInfoMapmapping bone names →BonePose(an integer ID +offsetMatrix), and a flatnodesvector ofSkeletonNode. The flat vector is topologically sorted — parents always appear before children — which lets the animator traverse it with a plainforloop instead of recursion. TheoffsetMatrixis the inverse of a bone's bind-pose world transform; it moves vertices from world space into the bone's local space before the animated transform is applied.animation.hpp—AnimationThin wrapper around
aiAnimation. The constructor reads duration, ticks-per-second (defaulting to 25 if assimp returns 0), and iteratesmChannelsto build aBoneAnimationper node, stored in achannelsmap keyed by node name.animator.hpp—Animator::computeBoneTransformThis is the core loop. For each node in topological order:
localTransformchannelshas an entry for this node name, replace it withchannel.interpolate(currentTime)globalTransforms[i] = globalTransforms[parentIndex] * localTransform(root useslocalTransformdirectly)nodeTransformsby name (for non-skinned nodes like attached meshes or lights)finalBoneMatrices[id] = globalInverseTransform * globalTransforms[i] * offsetMatrixfinalBoneMatricesis theuniformarray sent to the vertex shader. Each vertex stores up to N bone IDs and weights; the shader computes a weighted sum of those matrices to get the final vertex position.Things worth reviewing
MAX_BONES 110is a hard cap — worth confirming the target models don't exceed it, since overflow is silent (theid < finalBoneMatrices.size()check just skips the bone). Thespeedmultiplier inupdatescalesticksPerSeconddirectly, so aspeedof 2.0 doubles playback rate.isFinished()only fires on non-looping animations oncecurrentTime >= duration.note: if you didn't notice by now but this comment is generated by claude, if you need to understand anything ask me